package jme3

import com.jme3.app.BasicProfilerState
import com.jme3.app.DebugKeysAppState
import com.jme3.app.FlyCamAppState
import com.jme3.app.SimpleApplication
import com.jme3.app.StatsAppState
import com.jme3.app.state.ScreenshotAppState
import com.jme3.material.Material
import com.jme3.math.ColorRGBA
import com.jme3.math.Quaternion
import com.jme3.math.Vector3f
import com.jme3.renderer.RenderManager
import com.jme3.renderer.ViewPort
import com.jme3.scene.BatchNode
import com.jme3.scene.Geometry
import com.jme3.scene.Node
import com.jme3.scene.Spatial
import com.jme3.scene.control.AbstractControl
import com.jme3.scene.shape.Box
import java.util.Random

/**
 * Tests a deep scene with an unrecommended amount of objects.
 */
class Jme3DTestSceneStress :
    SimpleApplication(
        StatsAppState(), DebugKeysAppState(), BasicProfilerState(false),
        FlyCamAppState(),
        ScreenshotAppState("", System.currentTimeMillis())
    ) {
    private var mat: Material? = null
    private val random = Random(0)
    private var totalNodes = 0
    private var totalGeometry = 0
    private var totalControls = 0
    private var box = Box(2f, 0.5f, 0.5f)

    private var enableBatch = false
    private var enableShareMesh = true

    override fun simpleInitApp() {
        flyCam.isDragToRotate = true
        stateManager.getState(FlyCamAppState::class.java).camera.moveSpeed = 10f
        mat = Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md")

        // Create a deep, mostly static scene
        val oct = createOctSplit("root", 500, 5)
        rootNode.attachChild(oct)

        // Position to see most of it
        cam.location = Vector3f(400.8009f, 370.16455f, -408.17984f)
        cam.rotation = Quaternion(0.24906662f, -0.3756747f, 0.105560325f, 0.88639235f)
        println("Total nodes:$totalNodes  Total Geometry:$totalGeometry  Total controls:$totalControls")
    }

    protected fun createOctSplit(name: String, size: Int, depth: Int): Spatial {
        if (!enableShareMesh && !enableBatch) {
            box = Box(2f, 0.5f, 0.5f)
        }
        if (depth == 0) {
            // Done splitting
            val geom = Geometry(name, box)
            totalGeometry++
            if (!enableBatch) {
                val mat = mat!!.clone()
                mat.setColor("Color", ColorRGBA.randomColor())
                geom.material = mat
            }
            else {
                geom.material = mat
            }
            if (random.nextFloat() < 0.01) {
                val control =
                    RotatorControl(random.nextFloat(), random.nextFloat(), random.nextFloat())
                geom.addControl(control)
                totalControls++
            }
            return geom
        }
        val root: Node
        if (enableBatch) {
            root = BatchNode(name)
        }
        else {
            root = Node(name)
        }
        totalNodes++
        val half = size / 2
        val quarter = half * 0.5f
        for (i in 0..1) {
            val x = i * half - quarter
            for (j in 0..1) {
                val y = j * half - quarter
                for (k in 0..1) {
                    val z = k * half - quarter
                    val child = createOctSplit(
                        "$name($i, $j, $k)",
                        half, depth - 1
                    )
                    child.setLocalTranslation(x, y, z)
                    root.attachChild(child)
                }
            }
        }
        return root
    }

    private inner class RotatorControl(private vararg val rotate: Float) :
        AbstractControl() {
        override fun controlUpdate(tpf: Float) {
            if (spatial != null) {
                spatial.rotate(rotate[0] * tpf, rotate[1] * tpf, rotate[2] * tpf)
            }
        }

        override fun controlRender(rm: RenderManager, vp: ViewPort) {}
    }
}